package net.sansi.v3correctserver.service.photorec.rec;

import lombok.extern.slf4j.Slf4j;
import net.sansi.v3correctserver.entity.IntPoint;
import net.sansi.v3correctserver.entity.color.PhotoColor;
import net.sansi.v3correctserver.entity.image.RecImage;
import net.sansi.v3correctserver.util.CorrectUtil;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;

/**
 * Created by Fang
 * create time : 2025/1/10
 * IDE : IntelliJ IDEA
 */

@Slf4j
public class RecScanBoard {

    RecImage photo;
    int w, h;
    int[] DIR_X = {-1, 1, 1, -1};
    int[] DIR_Y = {-1, -1, 1, 1};
    List<List<IntPoint>> fences = new ArrayList<>();

    public RecScanBoard(RecImage photo) {
        this.photo = photo;
        w = photo.getWidth();
        h = photo.getHeight();
    }

    public List<List<IntPoint>> searchScanBoards() {
        int sx = w / 2;
        int sy = h / 2;
        Queue<IntPoint> queue = new LinkedList<>();
        IntPoint start = findStart();
        queue.add(findStart());
        log.info("[center] {}, [start] {}", new IntPoint(sx, sy), start);
        while (!queue.isEmpty()) {
            IntPoint poll = queue.poll();
            if (isVisited(poll)) continue;
            List<IntPoint> fence = searchFence(poll.getX(), poll.getY());
            if (isOutSide(fence)) continue;
            fences.add(fence);
            List<IntPoint> intPoints = nextStart(fence);
            for (IntPoint intPoint : intPoints) queue.add(intPoint);
        }
        return fences;
    }

    private IntPoint findStart() {
        int sx = w / 2;
        int sy = h / 2;
        int[] res = new int[]{sx, sy};
        for (int x = sx; x < sx + 50; x++) {
            for (int y = sy; y < sy + 50; y++) {
                if (photo.getPhotoColor(x, y).getG() < photo.getPhotoColor(res[0], res[1]).getG()) {
                    res = new int[]{x, y};
                }
            }
        }
        return new IntPoint(res[0], res[1]);
    }

    int[] DX = {0, 1, 0, -1};
    int[] DY = {1, 0, -1, 0};

    private List<IntPoint> nextStart(List<IntPoint> fence) {
        int h = fence.get(3).getY() - fence.get(0).getY();
        int w = fence.get(1).getX() - fence.get(0).getX();
        int cx = fence.get(0).getX() + w / 2;
        int cy = fence.get(0).getY() + h / 2;
        List<IntPoint> res = new ArrayList<>();
        for (int i = 0; i < 4; i++) {
            IntPoint p = new IntPoint(cx + DX[i] * w, cy + DY[i] * h);
            if (!isInPhoto(p) || isVisited(p)) continue;
            res.add(p);
        }
        return res;
    }

    private boolean isVisited(IntPoint p) {
        for (List<IntPoint> fence : fences) {
            if (CorrectUtil.isInFence(p.getX(), p.getY(), fence)) return true;
        }
        return false;
    }

    private boolean isOutSide(List<IntPoint> fence) {
        for (IntPoint point : fence) {
            if (point.getX() < 5 || point.getY() < 5 || point.getX() > w - 5 || point.getY() > h - 5) return true;
        }
        return false;
    }


    private List<IntPoint> searchFence(int startX, int startY) {
        ArrayList<IntPoint> fence = new ArrayList<>();
        for (int i = 0; i < 4; i++) {
            fence.add(new IntPoint(0, 0));
            searchTopPoint(startX, startY, i, fence);
        }
        return fence;
    }


    /**
     * 搜索某个方向上的极限顶点
     *
     * @param startX 开始点 x
     * @param startY 开始点 y
     * @param dir    方向
     * @param fence  点列表
     */
    private void searchTopPoint(int startX, int startY, int dir, List<IntPoint> fence) {
        IntPoint p0 = new IntPoint(startX, startY);

        if (!isInPhoto(p0)) return;

        IntPoint px = new IntPoint(startX, startY);
        IntPoint py = new IntPoint(startX, startY);

        while (px.getX() > -1 && px.getX() < w) {
            px.setX(px.getX() + DIR_X[dir]);
            if (px.getX() < 0 || px.getX() >= w || isGreen(px)) break;
        }

        while (py.getY() > -1 && py.getY() < h) {
            py.setY(py.getY() + DIR_Y[dir]);
            if (py.getY() < 0 || py.getY() >= h || isGreen(py)) break;
        }

        px.setX(Math.max(0, Math.min(w - 1, px.getX())));
        py.setY(Math.max(0, Math.min(h - 1, py.getY())));

        IntPoint cp = new IntPoint(
                (px.getX() + startX) >> 1,
                (py.getY() + startY) >> 1
        );

        fence.set(dir, cp);
        if (distance(p0, cp) < 1 || isGreen(cp)) return;

        searchTopPoint(cp.getX(), cp.getY(), dir, fence);
    }

    private boolean isGreen(PhotoColor color) {
        return color.getG() > 200 && color.getR() / color.getG() < 0.7 && color.getB() / color.getG() <= 1.2;
    }

    private boolean isGreen(IntPoint point) {
        return isGreen(photo.getPhotoColor(point.getX(), point.getY()));
    }

    /**
     * 计算两点距离
     *
     * @param p1 点1
     * @param p2 点2
     * @return 距离
     */
    int distance(IntPoint p1, IntPoint p2) {
        return Math.abs(p1.getX() - p2.getX()) + Math.abs(p1.getY() - p2.getY());
    }

    /**
     * 判断点是否在图片内
     *
     * @param x x 坐标
     * @param y y 坐标
     * @return 是否在图片内
     */
    private boolean isInPhoto(int x, int y) {
        return x >= 0 && x < w && y >= 0 && y < h;
    }

    /**
     * 判断点是否在图片内
     *
     * @param p 点
     * @return 是否在图片内
     */
    private boolean isInPhoto(IntPoint p) {
        return isInPhoto(p.getX(), p.getY());
    }


}
